package com.lizk.knowledge.minequeue;

import java.util.ArrayList;
import java.util.List;


/**
 * 模拟一个阻塞队列
 * @author lizk
 *
 */
public class MineQueue {
	int size = 20;//队列大小
	volatile List<Object> list = new ArrayList<Object>(size);//队列存储list
	volatile long head = -1l;//头部指针
	Object headLock = new Object();//头部指针锁
	volatile long tail = -1l;//尾部指针
	Object tailLock = new Object();//尾部指针锁
	
	List<Object> putList = new ArrayList<>();
	List<Object> takeList = new ArrayList<>();
	
	MineQueue(){
		for(int i = 0  ; i < size ; i ++){
			list.add(null);
		}
	}
	
	/**
	 * 进队方法
	 * @param object
	 * @return
	 */
	public boolean put(Object object){
		synchronized(tailLock){//对尾部指针锁进行锁定，保证对尾部指针的操作不会冲突
			long tmpTail = tail + 1; //暂时存进队以后指针位置
			int tmpIndex = (int)tmpTail % size;//暂存指针位置计算以后的脚标
			if(tail - head == 20){//判断队列是否已满，如果满了，那么在headLock锁上进行阻塞，如果没有，那么向队列中写入数据
				synchronized (headLock) {
					try {
						headLock.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			
			list.set(tmpIndex,object);
			
			
			/*
			 * 尾部指针要在数据写入队列以后在增加，防止take的时候判断指针的时候通过，但是队列中没有数据
			 * 因为tail是被  “volatile”修饰的，所以不会把tail++语句之前的语句重排到后面去，
			 * volatile有连个作用，①保证可见性，②禁止指令重排
			 */
			tail ++;
			
			
			
			//System.out.print(System.currentTimeMillis()+"写入=>" + object);
			putList.add(object);
			
			tailLock.notify();
			return true;
		}
	}
	
	public Object take(){
		synchronized (headLock) {//对headLock进行锁定，保证对head，头部指针的操作不冲突
			
			if(head >= tail){//判断队列是否已经空了，如果空了，那么在taillock上阻塞，等待写数据
				synchronized(tailLock){
					try {
						tailLock.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			
			head ++;//先增加指针，然后再取出队列里的数据，防止数据已经被取出，而put的时候判断队列是不是满了的时候出错。
			Object o = list.get((int)head%size);
			list.set((int)head%size, null);
			
			//System.out.print(System.currentTimeMillis()+"取出++++++++:>"+o);
			takeList.add(o);
			
			headLock.notify();
			return o;
		}
	}
	
}
